﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ClusterAggregation.ClusterAggregators
{
    using Datum;
    using DataSets;
    using Clusterers;

    /**
    * Implementation for Cluster-based Similarity Partitioning Algorithm (CSPA)
    * thoroughly explained at ( http://strehl.com/diss/node80.html )
    */
    public class CCSPA:IConsensus
    {
        /** private clusterer */
        private IClusterer m_clusterer;
        
        /** 
         * basic constructor 
         * @param clusterer to be used for meta-clustering. default (or null) is Spectral Clustering ( CSpectralClusterer ) with 0.67 as smoothing param
         */
        public CCSPA(IClusterer clstrr = null)
        {
            m_clusterer = clstrr;
            if (clstrr == null)
                m_clusterer = new CSpectralClusterer(null, 0.3, true);
        }

        public CPartition ensemble(CPartition[] partitions, AData[] data, int k)
        {
            CPartition finalPartition = new CPartition();
            CPartition tPartition = new CPartition();
            int n = data.Length;
            int numofclsuters = 0;
            double[,] clusterMatrix;
            double[,] distanceMatrix;
            int currCluster=0;
            lock (this)
            {
                // count objects 
                for (int i = 0; i < partitions.Length; i++)
                {
                    numofclsuters+=partitions[i]._clusters.Count;
                }

                // allocate matrix
                clusterMatrix = new double[n, numofclsuters];
                distanceMatrix = new double[n,n];
                
                // init cluster matrix
                for (int i = 0; i < n; i++)
                {
                    for (int j = 0; j < numofclsuters; j++)
                    {
                        clusterMatrix[i, j] = 0;
                    }
                }

                currCluster = 0;
                // calculate cluster matrix
                for (int i = 0; i < partitions.Length; i++)
                {
                    for (int j = 0; j < partitions[i]._clusters.Count; j++)
                    {
                        CCluster cl = partitions[i]._clusters[j];

                        for (int l = 0; l < cl._data.Count; l++)
                        {
                            clusterMatrix[cl._data[l].id, currCluster] = 1;
                        }
                        currCluster++; // curr cluster is the current cluster we're working on, we're going over every cluster each time incrementing
                        // outer 2 loops are just for that, currCluster is the actuall index
                    }
                }

                // calculate distance matrix
                for (int i = 0; i < n; i++)
                {
                    for (int j = 0; j < n; j++)
                    {
                        distanceMatrix[i, j] = 0;
                        for (int l = 0; l < partitions.Length; l++)
                        {
                            // row col mult
                            distanceMatrix[i, j] += clusterMatrix[i, l] * clusterMatrix[j, l]; // l is the running index for the vector
                        }
                    }
                }


                // get distance function from matrix
                ISimilarity tmpFunc = new CMatrixBasedDistance(distanceMatrix);
                finalPartition = this.m_clusterer.cluster(data, tmpFunc, k);
                tPartition = this.m_clusterer.cluster(data, tmpFunc, k);

            } // end of lock
            finalPartition.name = "CSPA";
            finalPartition.ANMI = finalPartition.compareTo(partitions);
            tPartition.name = "CSPA";
            tPartition.ANMI = finalPartition.compareTo(partitions);

            return finalPartition.ANMI > tPartition.ANMI ? finalPartition : tPartition;
        } // end of ensemble func
    }
}
